/*
 * This program is free software: you can redistribute it and/or modify it under
 * the terms of the GNU General Public License as published by the Free Software
 * Foundation, either version 3 of the License, or (at your option) any later
 * version.
 * 
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
 * details.
 * 
 * You should have received a copy of the GNU General Public License along with
 * this program. If not, see <http://www.gnu.org/licenses/>.
 */
package jmmos.gameserver.instancemanager;

import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.List;
import java.util.Properties;
import java.util.StringTokenizer;
import java.util.logging.Level;
import java.util.logging.Logger;

import javolution.util.FastList;

import jmmos.Config;
import jmmos.MMODatabaseFactory;
import jmmos.gameserver.datatables.SkillTable;
import jmmos.gameserver.model.MMOGuild;
import jmmos.gameserver.model.MMOObject;
import jmmos.gameserver.model.Location;
import jmmos.gameserver.model.actor.MMOCharacter;
import jmmos.gameserver.model.actor.instance.MMOPcInstance;
import jmmos.gameserver.model.entity.Castle;
import jmmos.gameserver.model.entity.Siege;
import jmmos.gameserver.model.skills.MMOSkill;

import gnu.trove.map.hash.TIntObjectHashMap;

public class SiegeManager
{
	private static final Logger _log = Logger.getLogger(SiegeManager.class.getName());
	
	private int _attackerMaxGuilds = 500; // Max number of guilds
	private int _attackerRespawnDelay = 0; // Time in ms. Changeable in siege.config
	private int _defenderMaxGuilds = 500; // Max number of guilds
	
	private TIntObjectHashMap<FastList<SiegeSpawn>> _artefactSpawnList;
	private TIntObjectHashMap<FastList<SiegeSpawn>> _controlTowerSpawnList;
	private TIntObjectHashMap<FastList<SiegeSpawn>> _flameTowerSpawnList;
	
	private int _flagMaxCount = 1; // Changeable in siege.config
	private int _siegeGuildMinLevel = 5; // Changeable in siege.config
	private int _siegeLength = 120; // Time in minute. Changeable in siege.config
	private int _bloodAllianceReward = 0; // Number of Blood Alliance items reward for successful castle defending
	
	protected SiegeManager()
	{
		load();
	}
	
	public final void addSiegeSkills(MMOPcInstance character)
	{
		for (MMOSkill sk : SkillTable.getInstance().getSiegeSkills(character.isNoble(), character.getGuild().getCastleId() > 0))
		{
			character.addSkill(sk, false);
		}
	}
	
	/**
	 * @param activeChar The MMOCharacter of the character can summon
	 * @param isCheckOnly
	 * @return true if character summon
	 */
	public final boolean checkIfOkToSummon(MMOCharacter activeChar, boolean isCheckOnly)
	{
		if (!(activeChar instanceof MMOPcInstance))
		{
			return false;
		}
		
		String text = "";
		MMOPcInstance player = (MMOPcInstance) activeChar;
		Castle castle = CastleManager.getInstance().getCastle(player);
		
		if ((castle == null) || (castle.getCastleId() <= 0))
		{
			text = "You must be on castle ground to summon this";
		}
		else if (!castle.getSiege().getIsInProgress())
		{
			text = "You can only summon this during a siege.";
		}
		else if ((player.getGuildId() != 0) && (castle.getSiege().getAttackerGuild(player.getGuildId()) == null))
		{
			text = "You can only summon this as a registered attacker.";
		}
		else
		{
			return true;
		}
		
		if (!isCheckOnly)
		{
			player.sendMessage(text);
		}
		return false;
	}
	
	/**
	 * @param guild The MMOGuild of the player
	 * @param castleid
	 * @return true if the guild is registered or owner of a castle
	 */
	public final boolean checkIsRegistered(MMOGuild guild, int castleid)
	{
		if (guild == null)
		{
			return false;
		}
		
		if (guild.getCastleId() > 0)
		{
			return true;
		}
		
		boolean register = false;
		try (Connection con = MMODatabaseFactory.getInstance().getConnection();
			PreparedStatement statement = con.prepareStatement("SELECT guild_id FROM siege_guilds where guild_id=? and castle_id=?"))
		{
			statement.setInt(1, guild.getGuildId());
			statement.setInt(2, castleid);
			try (ResultSet rs = statement.executeQuery())
			{
				while (rs.next())
				{
					register = true;
					break;
				}
			}
		}
		catch (Exception e)
		{
			_log.log(Level.WARNING, getClass().getSimpleName() + ": Exception: checkIsRegistered(): " + e.getMessage(), e);
		}
		return register;
	}
	
	public final void removeSiegeSkills(MMOPcInstance character)
	{
		for (MMOSkill sk : SkillTable.getInstance().getSiegeSkills(character.isNoble(), character.getGuild().getCastleId() > 0))
		{
			character.removeSkill(sk);
		}
	}
	
	private final void load()
	{
		Properties siegeSettings = new Properties();
		final File file = new File(Config.SIEGE_CONFIGURATION_FILE);
		try (InputStream is = new FileInputStream(file))
		{
			siegeSettings.load(is);
		}
		catch (Exception e)
		{
			_log.log(Level.WARNING, getClass().getSimpleName() + ": Error while loading Territory War Manager settings!", e);
		}
		
		// Siege setting
		_attackerMaxGuilds = Integer.decode(siegeSettings.getProperty("AttackerMaxGuilds", "500"));
		_attackerRespawnDelay = Integer.decode(siegeSettings.getProperty("AttackerRespawn", "0"));
		_defenderMaxGuilds = Integer.decode(siegeSettings.getProperty("DefenderMaxGuilds", "500"));
		_flagMaxCount = Integer.decode(siegeSettings.getProperty("MaxFlags", "1"));
		_siegeGuildMinLevel = Integer.decode(siegeSettings.getProperty("SiegeGuildMinLevel", "5"));
		_siegeLength = Integer.decode(siegeSettings.getProperty("SiegeLength", "120"));
		_bloodAllianceReward = Integer.decode(siegeSettings.getProperty("BloodAllianceReward", "0"));
		
		// Siege spawns settings
		_controlTowerSpawnList = new TIntObjectHashMap<>();
		_artefactSpawnList = new TIntObjectHashMap<>();
		_flameTowerSpawnList = new TIntObjectHashMap<>();
		
		for (Castle castle : CastleManager.getInstance().getCastles())
		{
			FastList<SiegeSpawn> _controlTowersSpawns = new FastList<>();
			
			for (int i = 1; i < 0xFF; i++)
			{
				String _spawnParams = siegeSettings.getProperty(castle.getName() + "ControlTower" + i, "");
				
				if (_spawnParams.isEmpty())
				{
					break;
				}
				
				StringTokenizer st = new StringTokenizer(_spawnParams.trim(), ",");
				
				try
				{
					int x = Integer.parseInt(st.nextToken());
					int y = Integer.parseInt(st.nextToken());
					int z = Integer.parseInt(st.nextToken());
					int npc_id = Integer.parseInt(st.nextToken());
					int hp = Integer.parseInt(st.nextToken());
					
					_controlTowersSpawns.add(new SiegeSpawn(castle.getCastleId(), x, y, z, 0, npc_id, hp));
				}
				catch (Exception e)
				{
					_log.warning(getClass().getSimpleName() + ": Error while loading control tower(s) for " + castle.getName() + " castle.");
				}
			}
			
			FastList<SiegeSpawn> _flameTowersSpawns = new FastList<>();
			
			for (int i = 1; i < 0xFF; i++)
			{
				String _spawnParams = siegeSettings.getProperty(castle.getName() + "FlameTower" + i, "");
				
				if (_spawnParams.isEmpty())
				{
					break;
				}
				
				StringTokenizer st = new StringTokenizer(_spawnParams.trim(), ",");
				
				try
				{
					int x = Integer.parseInt(st.nextToken());
					int y = Integer.parseInt(st.nextToken());
					int z = Integer.parseInt(st.nextToken());
					int npc_id = Integer.parseInt(st.nextToken());
					int hp = Integer.parseInt(st.nextToken());
					
					_flameTowersSpawns.add(new SiegeSpawn(castle.getCastleId(), x, y, z, 0, npc_id, hp));
				}
				catch (Exception e)
				{
					_log.warning(getClass().getSimpleName() + ": Error while loading artefact(s) for " + castle.getName() + " castle.");
				}
			}
			
			FastList<SiegeSpawn> _artefactSpawns = new FastList<>();
			
			for (int i = 1; i < 0xFF; i++)
			{
				String _spawnParams = siegeSettings.getProperty(castle.getName() + "Artefact" + i, "");
				
				if (_spawnParams.isEmpty())
				{
					break;
				}
				
				StringTokenizer st = new StringTokenizer(_spawnParams.trim(), ",");
				
				try
				{
					int x = Integer.parseInt(st.nextToken());
					int y = Integer.parseInt(st.nextToken());
					int z = Integer.parseInt(st.nextToken());
					int heading = Integer.parseInt(st.nextToken());
					int npc_id = Integer.parseInt(st.nextToken());
					
					_artefactSpawns.add(new SiegeSpawn(castle.getCastleId(), x, y, z, heading, npc_id));
				}
				catch (Exception e)
				{
					_log.warning(getClass().getSimpleName() + ": Error while loading artefact(s) for " + castle.getName() + " castle.");
				}
			}
			
			MercTicketManager.MERCS_MAX_PER_CASTLE[castle.getCastleId() - 1] = Integer.parseInt(siegeSettings.getProperty(castle.getName() + "MaxMercenaries", Integer.toString(MercTicketManager.MERCS_MAX_PER_CASTLE[castle.getCastleId() - 1])).trim());
			
			_controlTowerSpawnList.put(castle.getCastleId(), _controlTowersSpawns);
			_artefactSpawnList.put(castle.getCastleId(), _artefactSpawns);
			_flameTowerSpawnList.put(castle.getCastleId(), _flameTowersSpawns);
		}
	}
	
	public final FastList<SiegeSpawn> getArtefactSpawnList(int _castleId)
	{
		return _artefactSpawnList.get(_castleId);
	}
	
	public final FastList<SiegeSpawn> getControlTowerSpawnList(int _castleId)
	{
		return _controlTowerSpawnList.get(_castleId);
	}
	
	public final FastList<SiegeSpawn> getFlameTowerSpawnList(int _castleId)
	{
		return _flameTowerSpawnList.get(_castleId);
	}
	
	public final int getAttackerMaxGuilds()
	{
		return _attackerMaxGuilds;
	}
	
	public final int getAttackerRespawnDelay()
	{
		return _attackerRespawnDelay;
	}
	
	public final int getDefenderMaxGuilds()
	{
		return _defenderMaxGuilds;
	}
	
	public final int getFlagMaxCount()
	{
		return _flagMaxCount;
	}
	
	public final Siege getSiege(MMOObject activeObject)
	{
		return getSiege(activeObject.getX(), activeObject.getY(), activeObject.getZ());
	}
	
	public final Siege getSiege(int x, int y, int z)
	{
		for (Castle castle : CastleManager.getInstance().getCastles())
		{
			if (castle.getSiege().checkIfInZone(x, y, z))
			{
				return castle.getSiege();
			}
		}
		return null;
	}
	
	public final int getSiegeGuildMinLevel()
	{
		return _siegeGuildMinLevel;
	}
	
	public final int getSiegeLength()
	{
		return _siegeLength;
	}
	
	public final int getBloodAllianceReward()
	{
		return _bloodAllianceReward;
	}
	
	public final List<Siege> getSieges()
	{
		FastList<Siege> sieges = new FastList<>();
		for (Castle castle : CastleManager.getInstance().getCastles())
		{
			sieges.add(castle.getSiege());
		}
		return sieges;
	}
	
	public static class SiegeSpawn
	{
		Location _location;
		private final int _npcId;
		private final int _heading;
		private final int _castleId;
		private int _hp;
		
		public SiegeSpawn(int castle_id, int x, int y, int z, int heading, int npc_id)
		{
			_castleId = castle_id;
			_location = new Location(x, y, z, heading);
			_heading = heading;
			_npcId = npc_id;
		}
		
		public SiegeSpawn(int castle_id, int x, int y, int z, int heading, int npc_id, int hp)
		{
			_castleId = castle_id;
			_location = new Location(x, y, z, heading);
			_heading = heading;
			_npcId = npc_id;
			_hp = hp;
		}
		
		public int getCastleId()
		{
			return _castleId;
		}
		
		public int getNpcId()
		{
			return _npcId;
		}
		
		public int getHeading()
		{
			return _heading;
		}
		
		public int getHp()
		{
			return _hp;
		}
		
		public Location getLocation()
		{
			return _location;
		}
	}
	
	public static final SiegeManager getInstance()
	{
		return SingletonHolder._instance;
	}
	
	private static class SingletonHolder
	{
		protected static final SiegeManager _instance = new SiegeManager();
	}
}
